/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Produccion.Simulacion;

import com.sun.org.apache.bcel.internal.generic.AALOAD;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;

/**la estructura del siguiente beanFunction es la siguiente:
 * 1.Funcion principal
 * 2.Funciones para crear soluciones
 * 3.Funciones para hacer el paso generacional
 * 4.Funcion desempenho y utilidad
 *
 * @author Victor
 */
public class AlgMemeticoBeanFunction {
    //constantes para la presentacion del viernes
    private static final double PRECIOVENTA = 2;    
    private static final int DEMANDA = 1000;
    private static final int BONUSPORCEROMERMA = 100;
    private static final int ITCONDMESETA = 400;
    
    //variables estaticas del algoritmo(aun no se les da uso)
    private static int cambioMaquina=0;//indica si se cargo una nueva lista de maquinas
    private static int cambioTrabajador=0;//inidica si se cargo una nueva lista de trabajadores       
    
    private int itConvergencia=0;
    //El uso de hashmap se justifica en la rapidez de acceso a los datos y en ek orden aleatorio en el
    //cual se guardan los datos.
    private HashMap<String, TrabajadorBeanData> trabajadores;//lista de los datos de los trabajadores
    private ArrayList<String> CodTrabajadores;//lista de codigo de trabajadores
    
    private HashMap<String,MaquinaBeanData> maquinas;//lista de los datos de las maquinas
    private ArrayList<String> codMaquinas;//lista de codigo de trabajadores
    
    private HashMap<String,HashMap<String,Double>> desempenhoxTrabajadorxTipoMaquina=null;
    
    
    String tipoMaquinaActual;
    private TreeSet<Map.Entry<String,Double>> auxDesempenhoxTrabajadorxTipoMaquina;
    
    private ArrayList<String> tipoMaquinas;//lista de los tipos de maquina        
    //private HashMap<String,TipoMaquinaBeanData> tipoMaquinas;//lista de los tipos de maquina        
    
    private static SortedMap<String,Integer> maquinasxTipo;//contador de maquinas por cada tipo
    private SortedMap<String,Integer> auxMaquinasxTipo;//contador de maquinas por cada tipo, modificable
    
    private static SortedMap<String,Double> mermaxTipoMaquina;//merma por cada tipo de maquina    
            
    private int trabajadoresSize;//numero de trabajadores disponibles para asignar
    
    
    /**Funcion principal que ejcuta el algoritmo, devuelve un hashmap que contiene para cada maquina el trabajador asignado.
     * Recibe como parametros en tamaño de cada poblacion(numero de soluciones en la poblacion),el porcentaje de soluciones a conservar
     * cuando hay una convergencia y la catidad de pasos generacionales a hacer
     * 
     */
    public HashMap<String, String> ALG01(int n,double conservar,int iteracionesxturno,double alfa) throws Exception{         
               
        ArrayList<HashMap<String,String>> soluciones= new ArrayList<>();//guarda las soluciones generadas en el paso generacional anterior
        ArrayList<HashMap<String,String>> nuevassoluciones= new ArrayList<>();//guarda las nuevas solucines generadas en el paso generacional actual
        ArrayList<Object> auxiliarLst;//guarda 2 objetos:maximo rendimiento de todas la poblaciones hasta ahora y el rendimiento de todas las soluciones en la poblacion que se evaluo
        HashMap<String,String> auxiliar= new HashMap<>();//guarda temporalmente las soluciones de la poblacion inicial        
        TreeSet <Map.Entry<Integer,Double>> utilidadxSolucion;//
        boolean cParada=true;//en este caso solo es el numero de iteraciones        
        int iteraciones=0;    
        //calculamos la funcion de desempenho para todos los trabajadores por cada tipo de maquina
        if(this.cambioTrabajador!=0)calcularFuncionDesempenhoTrabajadores();
        //primera generacion                
        for(int j=0;j<n;j++){    
            auxiliar=this.GenerarSolucion(alfa);
            soluciones.add(auxiliar);            
        }                      
        auxiliarLst=this.promedioUtilidadPoblacion(soluciones);//sacamos el promedio de utilidad de la poblacion inicial
        double maxRendimientodPoblacional=((Double)auxiliarLst.get(0)).doubleValue();//aca se va guarda el maximo rendimiento 
                                                                                     //poblacional hasta ahora que es el de la poblacion inicial)
        utilidadxSolucion=(TreeSet <Map.Entry<Integer,Double>> )auxiliarLst.get(1);//Aca se guarda la utilidad de cada solucion en la poblacion inicial
        double rendimientoPoblacional=0;//guarda el rendmiento de una poblacion
        while(cParada){            
            nuevassoluciones=pasoGeneracional(soluciones);//se generan nuevas soluciones "casando" a las soluciones de la poblaciona anterior 
            auxiliarLst=this.promedioUtilidadPoblacion(nuevassoluciones);//sacamos el promedio de utilidad de la nueva poblacion
            rendimientoPoblacional=((Double)auxiliarLst.get(0)).doubleValue();//aca se va guarda el rendimiento de la nueva poblacion
            utilidadxSolucion=(TreeSet <Map.Entry<Integer,Double>> )auxiliarLst.get(1); // aca se guarda el rendimiento de cada solucion en la nueva poblacion
            if(rendimientoPoblacional<maxRendimientodPoblacional){ // se verifica si se llego a una convergencia(solucion nueva peor que la mejor de las anteriores)
                nuevassoluciones=this.reinicioPoblacion(nuevassoluciones,conservar,utilidadxSolucion,alfa);//se reinicia la poblacion(se reiran reemplazan peores soluciones por nuevas)
            }
            else maxRendimientodPoblacional=rendimientoPoblacional;//como la nueva solucion es mejor, su rendimiento es ahora el maximo                            
            soluciones=nuevassoluciones;//la nueva solucion se convierte en antigua
            iteraciones++;//contador de iteraciones
            cParada=(iteraciones>iteracionesxturno)?false:true;//se verifica la condicion de parada
            
        }
        return soluciones.get(utilidadxSolucion.last().getKey());// se retorna la mejor solucion
    }     
    /******En esta seccion estan las funciones dedicadas a generar una nueva solucion*******/
    
    /**Esta funcion se encarga de contar las maquinas que hay por cada tipo*/
     private void contarMaquinasxTipo() {
        TreeMap<String,Integer> nuevoMaquinasxTipo= new TreeMap<String, Integer>(); //guardas el numero de maquinas x tipo       
        TreeMap<String,Double> nuevoMermaxTipoMaquina= new TreeMap<String, Double>(); //guarda el costo de la merma maxima de las maquinas x cada tipo
        Iterator it = this.maquinas.entrySet().iterator();
        String tipoMaquina;//guarda el tipo de maquina en cada iteracion
        Double costoMerma;//guarda el costo de merma en cada iteracion
        while(it.hasNext()){//hasta que se acabe la lista de maquinas
            MaquinaBeanData maquina=(MaquinaBeanData)((Map.Entry)it.next()).getValue();//guarda todos lo datos de la maquina
            tipoMaquina=maquina.getTipo();//se guarda solo el tipo de maquina
            costoMerma=Double.valueOf(maquina.getCostoMerma());//se guarda solo el costo de la merma de la maquina
            nuevoMaquinasxTipo.put(tipoMaquina, (nuevoMaquinasxTipo.get(tipoMaquina)==null?0:nuevoMaquinasxTipo.get(tipoMaquina))+1);// se aumenta el contador de maquinas para el tipo maquina encontrado            
            if(nuevoMermaxTipoMaquina.get(tipoMaquina)==null || costoMerma>nuevoMermaxTipoMaquina.get(tipoMaquina))//se guarda siempre la merma maxima x cada tipo
                nuevoMermaxTipoMaquina.put(tipoMaquina, costoMerma);            
        }
        mermaxTipoMaquina=nuevoMermaxTipoMaquina;//se asigna el arreglo de mermas a la variable global de la funcion
        maquinasxTipo=nuevoMaquinasxTipo;//se asigna el arreglo de numero de maquinas a la variable global de la funcion
        this.cambioMaquina=0;//indica que ya se proceso la nueva lista de maquinas
    }    
     
    /** Esta funcion selecciona aleatoriamente un trabajador, esa es su unica funcion */
    private String SeleccionarTrabajador() {
        Random rnd= new Random();// esta clase es capaz de generar numeros aleatoriamente        
        try{
        int rndInt= rnd.nextInt(trabajadoresSize);// se genera un numero aleatorio entre en rango{0,trabajadoresSize-1}
        String seleccion=getCodTrabajadores().get(rndInt); //se selecciona el codigo del trabajador de acuerdo al numero aleatorio
        getCodTrabajadores().remove(rndInt);//se remueve el codigo del trabajador de los trabajadores sin asignar
        trabajadoresSize--;//se reduce el numero de trabajadores sin asignar
        return seleccion ;//se retorna el codigo elegido
        }
        catch(Exception e){
        return null;
        }
        
    }
    /**Esta funcion se encarga de asignar un tipo de maquina a cada trabajador*/
    private String asignarTipoMaquina(String codTrabajador,double alfa) throws Exception {
        TrabajadorBeanData trabajador=this.trabajadores.get(codTrabajador);// se carga los datos del trabajador
        Vector<RendimientoBeanData> rendimientoTrabajador=trabajador.getRendimiento();//se carga los rendimientos del trabajador
        Double maximoDesenpenho=null,minimoDesempenho=null;//se guardan maximo y minimo desempenho
        HashMap<String,Integer> indices=trabajador.getIndice();//indica en que indice del vector esta el rendimiento e cada tipo
        TreeSet<Map.Entry<Double,String>> desempenhoxTipo= new TreeSet<>(new ComparadorBeanFunction());//guarda de forma ordenada los tipos de maquina de acuerdo al desempenho del trabajador
        Iterator<String> it=tipoMaquinas.iterator();//recorre los valores del arraylist
        String tipoMaquina;//guarda el tipo de maquina para cada iteracion
        while(it.hasNext()){//se calcula maximo y minimo desempenho            
           tipoMaquina=it.next();           
           Double desempenho=this.funcionDesempenho(codTrabajador, tipoMaquina);//desenpenho del trabajdor con el tipo de maquina actual
           if(maximoDesenpenho==null || maximoDesenpenho.compareTo(desempenho)<0) maximoDesenpenho=desempenho;//verifica si un desempenho maximo
           if(minimoDesempenho==null || minimoDesempenho.compareTo(desempenho)>0) minimoDesempenho=desempenho;//verifica si un desempenho minimo
           desempenhoxTipo.add(new MapKeyBeanData<Double,String>(desempenho, tipoMaquina));//guarda el desempenho en una coleccion
        }
        Double limiteInferiorRCL=maximoDesenpenho-alfa*(maximoDesenpenho-minimoDesempenho);//Se calcula el limite inferior del RCL
        Integer cantidadDeMaquinasEnRCL=0; 
        Iterator itDesempenhoxTipo=desempenhoxTipo.descendingIterator();//recorre los valores de manera descendente
        while(itDesempenhoxTipo.hasNext()){//se van agragando la cantidad de maquinas que pueden estar en el RCL
            Map.Entry e=(Map.Entry)itDesempenhoxTipo.next();
            if (((Double)(e.getKey())).compareTo(limiteInferiorRCL)<0 && cantidadDeMaquinasEnRCL>0) break;//si ya se agrego todas la maquinas en el RCL se sale
            cantidadDeMaquinasEnRCL+=this.auxMaquinasxTipo.get((String)(e.getValue()));                   //pero si aun no se encuentra alguna maquina, se agrea las maquinas fuera del RCL
        } 
        cantidadDeMaquinasEnRCL=cantidadDeMaquinasEnRCL==0?1:cantidadDeMaquinasEnRCL;//Solo para asegurarnos que se asignen todas las maquians a los trabjadores       
        Random rnd= new Random();                
        Integer rndInt= rnd.nextInt(cantidadDeMaquinasEnRCL)+1;//se elige una maquina aleatoria del RCL
        itDesempenhoxTipo=desempenhoxTipo.descendingIterator();
        String tipoAsignado=null;
        int i=0;
        try{
        while(rndInt>0 ){//se selecciona el tipo de maquina correspondiente a la maquina seleccionda
            i++;
            Map.Entry e=(Map.Entry)itDesempenhoxTipo.next();            
            rndInt-=this.auxMaquinasxTipo.get((String)(e.getValue()));
            if(rndInt<=0) tipoAsignado=(String)(e.getValue());
        }
        }
        catch(Exception e){
            throw  new Exception("Error al asignar tipo de maquina al empleado:"+i+"/"+desempenhoxTipo.size()+"\\"+rndInt);
        }
        
        
        this.auxMaquinasxTipo.put(tipoAsignado,this.auxMaquinasxTipo.get(tipoAsignado)-1); // se reduce en 1 el numero de maquinas sin asignar para el tipo seleccionado
        
        return tipoAsignado;                
    }
    
    /**Como su nombre lo dice, esta funcion genera una solucion*/
//    public HashMap<String,String>GenerarSolucion(double alfa) throws Exception{
//                
//        HashMap<String,ArrayList<String>> trabajadorxTipo= new HashMap<>();//aca se guardara los codigos de la trabajadores por cada tipo de maquina
//        if(auxMaquinasxTipo==null) auxMaquinasxTipo=new TreeMap<>();// se hace una copia de las maquinas x tipo
//        auxMaquinasxTipo.clear();                                   // para no alterar la cuenta original y reusarla en        
//        auxMaquinasxTipo.putAll(maquinasxTipo);                     // la generacion de otras soluciones
//        
//        trabajadoresSize=CodTrabajadores.size();//renicia el numero de trabajadores
//        int maquinaSize=codMaquinas.size();//reinicia el numero de maquinas        
//        HashMap<String,String> solucion= new HashMap<>(); //aca se guardara la solucion generada(asignacion maquina-trabajador)       
//        
//        for(int i=0;i<maquinaSize;i++){
//            
//            String codTrabajador= this.SeleccionarTrabajador(); //se selecciona un trabajador al azar          
//            if(codTrabajador!=null) {
//                String tipoMaquinaAsignado=this.asignarTipoMaquina(codTrabajador,alfa);//se obtiene el tipo asignado al trabajador
//                if(trabajadorxTipo.get(tipoMaquinaAsignado)==null) {//se guarda el tipo asignado en un arraylist
//                    ArrayList<String> trabajadores= new ArrayList<>();
//                    trabajadores.add(codTrabajador);                    
//                    trabajadorxTipo.put(tipoMaquinaAsignado, trabajadores);
//                }
//                else{
//                    trabajadorxTipo.get(tipoMaquinaAsignado).add(codTrabajador);
//                }                
//            }          
//            else break;
//        };        
//        for(int i=0;i<maquinaSize;i++){//de acuerdo al tipo asignado al trabajador se le asgina una maquina
//            String codMaquina=codMaquinas.get(i);
//            String tipoMaquina=maquinas.get(codMaquina).getTipo();
//            solucion.put(codMaquina, trabajadorxTipo.get(tipoMaquina).get(0));
//            trabajadorxTipo.get(tipoMaquina).remove(0);            
//        }
//        this.setCodTrabajadores(new ArrayList<String>(trabajadores.keySet()));//se reinicia la lista de codigos de trabajdor disponibles para la siguieten genracion de solucion
//        return solucion;
//    }
    
    
   /******En esta seccion estan las funciones dedicadas al paso genracional*******/  
 
    /**Esta funcion se dedica a "casar" a las soluciones y devielve sus "hijos"*/
    private ArrayList<HashMap<String,String>> reproducir(HashMap<String,String> sol1,HashMap<String,String> sol2){
    //cruze multipunto
        Iterator it = sol1.entrySet().iterator();//recorre la solucion 1
        HashMap<String,String> nsol1= new HashMap<>();//se guarda el hijo 1
        HashMap<String,String> nsol2= new HashMap<>();//se guarda el hijo 2
        HashMap<String, Integer> trabajadorSol= new HashMap<>();
        ArrayList<HashMap<String,String>> resultado= new ArrayList<>();
        while(it.hasNext()){
            Map.Entry e = (Map.Entry)it.next();
            String codMaquina=(String)e.getKey();//se obtiene un codigo de maquina
            if(trabajadorSol.get(sol1.get(codMaquina))==null && trabajadorSol.get(sol2.get(codMaquina))==null ){
                /*Se compara para esa maquina que solucion asigno al mejor trabajador de acuerdo a la funcion de desempenho*/
                if(funcionDesempenhoReal(codMaquina, sol1.get(codMaquina)).compareTo(funcionDesempenhoReal(codMaquina, sol2.get(codMaquina)))>=0){                
                    nsol1.put(codMaquina, sol1.get(codMaquina));
                    nsol2.put(codMaquina, sol2.get(codMaquina));
                    trabajadorSol.put(sol1.get(codMaquina), 1);
                    trabajadorSol.put(sol2.get(codMaquina), 2);
                }
                else{                    
                    nsol1.put(codMaquina, sol1.get(codMaquina));
                    nsol2.put(codMaquina, sol2.get(codMaquina));
                }
            }
            /*en caso el trabajador ya se le ha asignado en una comparacion anterior una maquina, se asegura que en una solucion no haya un trabajador a 2 maquinas*/
            else{
                    if(trabajadorSol.get(sol1.get(codMaquina))==1 && trabajadorSol.get(sol2.get(codMaquina))==2){
                        nsol2.put(codMaquina, sol1.get(codMaquina));
                        nsol1.put(codMaquina, sol2.get(codMaquina));            
                    }
                    else {
                        nsol1.put(codMaquina, sol1.get(codMaquina));
                        nsol2.put(codMaquina, sol2.get(codMaquina));            
                    }
            }
        }
        resultado.add(sol1);
        resultado.add(sol2);
        return resultado;
    }
    /**Esta funcion segun el % a conservar indicado, reemplaza las peores soluciones por nuevas*/
     private ArrayList<HashMap<String, String>> reinicioPoblacion(ArrayList<HashMap<String, String>> soluciones,double conservar,TreeSet <Map.Entry<Integer,Double>> utilidadxSolucion,double alfa) throws Exception {
         int retirar=(int)(soluciones.size()-soluciones.size()*conservar);//cantidad de soluciones a retirar
         ArrayList<HashMap<String, String>> retirados= new ArrayList<>();//guarda las soluciones a retirar
         ArrayList<HashMap<String, String>> nuevos= new ArrayList<>();//guarda las soluciones nuevas
         TreeSet <Map.Entry<Integer,Double>> temporal=utilidadxSolucion;//guarda de manera ordenada las soluciones, de acuerda a su rendimiento
         for(int i=0;i<retirar;i++){
         nuevos.add(this.GenerarSolucion(alfa));//se crean nuevas soluciones
         }
         Double minUtilidad=null;
         Iterator it= temporal.iterator();
         while(retirar>0){
             Map.Entry<Integer,Double> aRetirar=(Map.Entry<Integer,Double>)it.next();//se seleccionan las soluciones a retirar
             retirados.add(soluciones.get(aRetirar.getKey()));
             //temporal.remove(temporal.first());
             retirar--;
         }
         soluciones.removeAll(retirados);//se retiran las soluciones
         soluciones.addAll(nuevos);//se agregan las soluciones
         
        return soluciones;
    }
    /**esat funcion ejecuta todos los pasos para crear una nueva solucion a partir de una anterior*/
    private ArrayList<HashMap<String, String>> pasoGeneracional(ArrayList<HashMap<String, String>> soluciones) {
       Random rnd= new Random();                
       HashMap<String, String> auxiliar1,auxiliar2;
       ArrayList<HashMap<String, String>> resultado= new ArrayList<>();//guarda la nueva poblacion
       ArrayList<HashMap<String, String>> auxiliar= new ArrayList<>();//guarda ls nuevas soluciones
       int numAleatorio;
       while(soluciones.size()>0){           
           numAleatorio= rnd.nextInt(soluciones.size()>1?soluciones.size()-1:1);
           auxiliar1=soluciones.get(numAleatorio);//se selecciona una solucion al azar para que se reproduzca
           soluciones.remove(auxiliar1);//y se la retira de de la poblacion antigua
           if(soluciones.size()==0){//en caso el numero de la poblacion es impar la ultima solucion no se reproduce, para directamente a la nueva poblacion
               resultado.add(auxiliar1);
           }
           else{
           numAleatorio= rnd.nextInt(soluciones.size()>1?soluciones.size()-1:1);
           auxiliar2=soluciones.get(numAleatorio);//se selecciona una solucion al azar para que se reproduzca
           soluciones.remove(auxiliar2);//y se la retira de de la poblacion antigua        
           auxiliar=reproducir(auxiliar1,auxiliar1);
           
           resultado.addAll(auxiliar);//se agregan las nuevas soluciones a la nueva poblacion                              
           }
       }       
       return resultado;
    }
    
     /******En esta seccion estan las funciones de desempenho y de utilidad*******/  
    
    /**esta funcion  encuentra el desempenho real de los trabajdores con el tipo de maquina*/
    private Double funcionDesempenhoReal(String codMaquina,String codTrabajador){
        MaquinaBeanData maquina=maquinas.get(codMaquina);
        TrabajadorBeanData trabajador= trabajadores.get(codTrabajador);
        Double produccion= trabajador.getRendimiento().get(trabajador.getIndice().get(maquina.getTipo())).getProduccion();        
        Double perdida=trabajador.getRendimiento().get(trabajador.getIndice().get(maquina.getTipo())).getPerdida();        
        if(produccion==0 && perdida>0) return Double.valueOf("-1");
        else if(produccion==0 && perdida==0) return Double.valueOf("0");
        else if(perdida==0) return(BONUSPORCEROMERMA * (produccion *PRECIOVENTA));;        
        Double resultado=(produccion/perdida)*(produccion*PRECIOVENTA-maquina.getCostoMerma());                
        
    return resultado;
    }
    
    /**esta funcion  encuentra el desempenho de los trabajdores con el tipo de maquina, usando el costo de merma maximo x tipo de maquina*/
    private Double funcionDesempenho(String codTrabajador,String tipo){
        TrabajadorBeanData trabajador= trabajadores.get(codTrabajador);
        Double produccion= trabajador.getRendimiento().get(trabajador.getIndice().get(tipo)).getProduccion();        
        Double perdida=trabajador.getRendimiento().get(trabajador.getIndice().get(tipo)).getPerdida();        
        if(produccion==0 && perdida>0) return Double.valueOf("-1");
        else if(produccion==0 && perdida==0) return Double.valueOf("0");
        else if(perdida==0) return(BONUSPORCEROMERMA * (produccion *PRECIOVENTA));;        
        Double resultado=(produccion/perdida)*(produccion*PRECIOVENTA-this.mermaxTipoMaquina.get(tipo));
    return resultado;
    }
    /**Esta funcion calcula el desempenho dado un rendimiento de trabjador*/
    private Double funcionDesempenho(RendimientoBeanData rendimiento,String tipo){        
        Double produccion= rendimiento.getProduccion();
        Double perdida=rendimiento.getPerdida();        
        if(produccion==0 && perdida>0) return Double.valueOf("-1");
        else if(produccion==0 && perdida==0) return Double.valueOf("0");
        else if(perdida==0) return(BONUSPORCEROMERMA * (produccion *PRECIOVENTA));;        
        Double resultado=(produccion/perdida)*(produccion*PRECIOVENTA-this.mermaxTipoMaquina.get(tipo));
    return resultado;
    }
    
    /**esta funcion encuentra la utilidad total de una solucion(funcion de fitness)*/
     public double funcionUtilidad(HashMap<String,String> Solucion){
        Iterator it = Solucion.entrySet().iterator();
        double Costo=0,Venta=0;   
        String codMaquina;
        while(it.hasNext()){
         codMaquina=(String)((Map.Entry)it.next()).getKey();  
         MaquinaBeanData maquina=maquinas.get(codMaquina);
         TrabajadorBeanData trabajador=trabajadores.get(Solucion.get(codMaquina));
         RendimientoBeanData rendTrabjador=trabajador.getRendimiento().get(trabajador.getIndice().get(maquina.getTipo()));               
         if("3".equals(maquina.getTipo())){
             //Venta +=  (PRECIOVENTA * (maquina.getProduccion()* rendTrabjador.getProduccion()-maquina.getProduccion()* rendTrabjador.getProduccion() *rendTrabjador.getPerdida()));          
             Venta +=  (PRECIOVENTA * (maquina.getProduccion()* rendTrabjador.getProduccion()));          
         }
         else Venta+= (maquina.getCostoMerma() * (maquina.getProduccion()* rendTrabjador.getProduccion()));
            //Costo += maquina.getProduccion()* rendTrabjador.getProduccion() *rendTrabjador.getPerdida()* maquina.getCostoMerma();
            Costo += rendTrabjador.getPerdida()* maquina.getCostoMerma();
        }
            return Venta-Costo;
    } 
      /**Esta funcion obtiene el promedio de utilida para una poblacion*/
     public ArrayList<Object> promedioUtilidadPoblacion(ArrayList<HashMap<String, String>> soluciones){
         double suma=0;
         double utilidadSolucion;
         ArrayList<Object> resultado= new ArrayList<>();
         TreeSet<Map.Entry<Integer,Double>> auxiliarUtilidadxPoblacion= new TreeSet<Map.Entry<Integer,Double>>(new ComparadorBeanFunction());
         HashMap<Integer,Double> temporal= new HashMap<>();                 
         for(int i=0;i<soluciones.size();i++){//se suman las utilidades de las soluciones en una poblacion
             utilidadSolucion=this.funcionUtilidad(soluciones.get(i));
             temporal.put(i,utilidadSolucion);
             suma+=utilidadSolucion;
         }
         Iterator it=temporal.entrySet().iterator();
         while(it.hasNext()){
         auxiliarUtilidadxPoblacion.add((Map.Entry<Integer,Double>)it.next());
         }
         resultado.add(Double.valueOf(suma/soluciones.size()));//se guarda el promedio de utilidad
         resultado.add(auxiliarUtilidadxPoblacion);//se guarda la utilidad de cada solucion en la poblacion
         return resultado;
             
     }
     
        //SETTERS AND GETTERS

    /**
     * @return the trabajadores
     */
    public HashMap<String, TrabajadorBeanData> getTrabajadores() {
        return trabajadores;
    }

    /**
     * @param trabajadores the trabajadores to set
     */
    public void setTrabajadores(HashMap<String, TrabajadorBeanData> trabajadores) {
        this.cambioTrabajador=1;//se cambio la lista de trabajadores
        this.setCodTrabajadores(new ArrayList<>(trabajadores.keySet()));
        this.trabajadores = trabajadores;
    }

    /**
     * @return the codMaquinas
     */
    public ArrayList<String> getCodMaquinas() {
        return codMaquinas;
    }

    /**
     * @param codMaquinas the codMaquinas to set
     */
    private void setCodMaquinas(ArrayList<String> codMaquinas) {
        this.codMaquinas = codMaquinas;
    }

    /**
     * @return the maquinasDisponibles
     */
    public HashMap<String,MaquinaBeanData> getMaquinasDisponibles() {
        return maquinas;
    }

    /**
     * @param maquinasDisponibles the maquinasDisponibles to set
     */
    public void setMaquinasDisponibles(HashMap<String,MaquinaBeanData> maquinasDisponibles) {
        this.cambioMaquina=1;//se cambio la lista de maquinas
        this.setCodMaquinas(new ArrayList<>(maquinasDisponibles.keySet()));        
        this.maquinas = maquinasDisponibles;
        this.contarMaquinasxTipo();//si se cambio la lista de maquina se cuentan de nuevo
    }

    /**
     * @return the CodTrabajadores
     */
    public ArrayList<String> getCodTrabajadores() {
        return CodTrabajadores;
    }

    /**
     * @param CodTrabajadores the CodTrabajadores to set
     */
    private void setCodTrabajadores(ArrayList<String> CodTrabajadores) {        
        this.CodTrabajadores = CodTrabajadores;
    }

    /**
     * @return the tipoMaquinas
     */
    public ArrayList<String> getTipoMaquinas() {
        return tipoMaquinas;
    }

    /**
     * @param tipoMaquinas the tipoMaquinas to set
     */
    public void setTipoMaquinas(ArrayList<String> tipoMaquinas) {
        this.tipoMaquinas = tipoMaquinas;
    }

    private void calcularFuncionDesempenhoTrabajadores() {
        int sizeM=tipoMaquinas.size();
        int sizeT=CodTrabajadores.size();
        desempenhoxTrabajadorxTipoMaquina= new HashMap<>();        
        for(int j=0;j<sizeT;j++){
            String codTrabajador=CodTrabajadores.get(j);
            Vector<RendimientoBeanData> rendimiento=trabajadores.get(codTrabajador).getRendimiento();
            HashMap<String,Integer> indice=trabajadores.get(codTrabajador).getIndice(); 
            HashMap<String,Double> desempenho=new HashMap<>();
            for(int i=0;i<sizeM;i++){                
                String tipoMaquina=tipoMaquinas.get(i);            
                desempenho.put(tipoMaquina, this.funcionDesempenho(rendimiento.get(indice.get(tipoMaquina)), tipoMaquina));                                           
            }
            desempenhoxTrabajadorxTipoMaquina.put(codTrabajador, desempenho);
        }
        this.cambioTrabajador=0;
    }
    
        public HashMap<String,String>GenerarSolucion(double alfa) throws Exception{
                
        HashMap<String,ArrayList<String>> trabajadorxTipo= new HashMap<>();//aca se guardara los codigos de la trabajadores por cada tipo de maquina
        if(auxMaquinasxTipo==null) auxMaquinasxTipo=new TreeMap<>();// se hace una copia de las maquinas x tipo
        auxMaquinasxTipo.clear();                                   // para no alterar la cuenta original y reusarla en        
        auxMaquinasxTipo.putAll(maquinasxTipo);                     // la generacion de otras soluciones
        
        //auxdesempenhoxTrabajadorxTipoMaquina= new HashMap<>(this.desempenhoxTrabajadorxTipoMaquina);
        
        trabajadoresSize=CodTrabajadores.size();//renicia el numero de trabajadores
        int maquinaSize=codMaquinas.size();//reinicia el numero de maquinas        
        HashMap<String,String> solucion= new HashMap<>(); //aca se guardara la solucion generada(asignacion maquina-trabajador)       
        int k=0;
        Iterator it=auxMaquinasxTipo.entrySet().iterator();
        Map.Entry<String,Integer> e=(Map.Entry<String,Integer>)it.next();
        tipoMaquinaActual=null;
        while(true){
            k++;            
            if(k>e.getValue()){
                k=0;
                if(!it.hasNext()) break;
                e=(Map.Entry<String,Integer>)it.next();                
            }
            String codTrabajador=this.asignarTipoMaquinaATrabajador(alfa,e.getKey());    
            if(codTrabajador!=null) {                
                if(trabajadorxTipo.get(e.getKey())==null) {//se guarda el tipo asignado en un arraylist
                    ArrayList<String> trabajadores= new ArrayList<>();
                    trabajadores.add(codTrabajador);                    
                    trabajadorxTipo.put(e.getKey(), trabajadores);
                }
                else{
                    trabajadorxTipo.get(e.getKey()).add(codTrabajador);
                }    
                getCodTrabajadores().remove(codTrabajador);
            }          
            else break;
        };        
        for(int i=0;i<maquinaSize;i++){//de acuerdo al tipo asignado al trabajador se le asgina una maquina
            String codMaquina=codMaquinas.get(i);
            String tipoMaquina=maquinas.get(codMaquina).getTipo();
            solucion.put(codMaquina, trabajadorxTipo.get(tipoMaquina).get(0));
            trabajadorxTipo.get(tipoMaquina).remove(0);            
        }
        this.setCodTrabajadores(new ArrayList<String>(trabajadores.keySet()));//se reinicia la lista de codigos de trabajdor disponibles para la siguieten genracion de solucion
        return solucion;
    }

    private String asignarTipoMaquinaATrabajador(double alfa, String tipoMaquina) {
        //Hallamos el limite inferior del RCL
        Double mayor=null,menor=null;        
        Iterator <String> it=this.getCodTrabajadores().iterator();
        if(tipoMaquinaActual==null|| !tipoMaquinaActual.equals(tipoMaquina)){
            auxDesempenhoxTrabajadorxTipoMaquina= new TreeSet<>(new ComparadorBeanFunction());
            while(it.hasNext()){                
                String codTrabajador=it.next();
                HashMap<String,Double> e=desempenhoxTrabajadorxTipoMaquina.get(codTrabajador);
                Double desempenho=e.get(tipoMaquina);
                if(mayor==null|| desempenho.compareTo(mayor)>0) mayor=desempenho;
                if(menor==null|| desempenho.compareTo(menor)<0) menor=desempenho;
                auxDesempenhoxTrabajadorxTipoMaquina.add(new MapKeyBeanData<String,Double>(codTrabajador, desempenho));                
            }
            tipoMaquinaActual=tipoMaquina;
        }
        else{
            menor=auxDesempenhoxTrabajadorxTipoMaquina.first().getValue();
            mayor=auxDesempenhoxTrabajadorxTipoMaquina.last().getValue();            
        }        
        Double limiteInferiorRCL=mayor-(mayor-menor)*alfa;
        Integer contador=0;
        Iterator<Map.Entry<String,Double>> itDesempenho=auxDesempenhoxTrabajadorxTipoMaquina.descendingIterator();
        while(itDesempenho.hasNext()){
            Double desempenho=itDesempenho.next().getValue();
            if(desempenho.compareTo(limiteInferiorRCL)>=0) contador++;
            else break;        
        }
        Random rnd= new Random();
        Integer rndInt=null;
        try{
            rndInt=rnd.nextInt(contador);            
        }
        catch(Exception e){}
        itDesempenho=auxDesempenhoxTrabajadorxTipoMaquina.descendingIterator();
        String codTrabajador=null;        
        contador=0;
        Map.Entry<String,Double> auxE=null;
        while(itDesempenho.hasNext()){
            auxE=itDesempenho.next();
            codTrabajador=auxE.getKey();
            if(contador<rndInt) contador++;
            else break;        
        } 
        auxDesempenhoxTrabajadorxTipoMaquina.remove(auxE);
        return codTrabajador;
    }

       
}
